#include <assert.h>
#include "message_queue.h"

int mq_init(mq* mq)
{
	if (list_init(&(mq->l)) != LIST_SUCCESS) {
		assert(0);
		return MQ_ERROR;
	}

	if (pthread_mutex_init(&(mq->lock), NULL) != 0) {
		assert(0);
		list_destroy(&(mq->l));
		return MQ_ERROR;
	}

	if (pthread_cond_init(&(mq->cond), NULL) != 0) {
		assert(0);
		list_destroy(&(mq->l));
		pthread_mutex_destroy(&(mq->lock));
		return MQ_ERROR;
	}

	return MQ_SUCCESS;
}

int mq_enqueue(mq* mq, mq_message m)
{
	int ret = MQ_ERROR;

	if (mq == NULL) {
		goto out;
	}

	if (pthread_mutex_lock(&(mq->lock)) != 0) {
		assert(0);
		goto out;
	}

	if (list_insert_tail(&(mq->l), m) != LIST_SUCCESS) {
		assert(0);
		goto unlock;
	}

	if (pthread_cond_signal(&(mq->cond)) != 0) {
		assert(0);
		list_remove_tail(&(mq->l));
		goto unlock;
	}

	ret = MQ_SUCCESS;

unlock:
	pthread_mutex_unlock(&(mq->lock));

out:
	return ret;
}

int mq_dequeue(mq* mq, mq_message* m, int block)
{
	iterator it;
	int lrc;
	int ret = MQ_ERROR;

	if (mq == NULL) {
		goto out;
	}

	if (pthread_mutex_lock(&(mq->lock)) != 0) {
		assert(0);
		goto out;
	}

	while(block && list_get_size(&(mq->l)) == 0) {
		if (pthread_cond_wait(&(mq->cond), &(mq->lock)) != 0) {
			assert(0);
			goto out;
		}
	}

	lrc = list_get_head(&(mq->l), &it);
	if (lrc == LIST_ERROR) {
		assert(0);
		goto unlock;
	}

	ret = MQ_EMPTY;
	if (lrc == LIST_END) {
		goto unlock;
	}

	ret = MQ_ERROR;
	lrc = iterator_get_value(&it, (list_element_t *) m);
	if (lrc != LIST_SUCCESS) {
		assert(0);
		goto unlock;
	}

	lrc = list_remove_head(&(mq->l));
	assert(lrc == LIST_SUCCESS);

	ret = MQ_SUCCESS;

unlock:
	pthread_mutex_unlock(&(mq->lock));

out:
	return ret;
}

int mq_destroy(mq* mq)
{
	int lrc, mrc;

	if (mq == NULL) {
		return MQ_ERROR;
	}

	lrc = list_destroy(&(mq->l));
	assert(lrc == LIST_SUCCESS);

	mrc = pthread_mutex_destroy(&(mq->lock));
	assert(mrc == 0);

	mrc = pthread_cond_destroy(&(mq->cond));
	assert(mrc == 0);

	return MQ_SUCCESS;
}
